namespace Bread.IoC;

public interface IHiddenInIoC { }

public class Controller : IHiddenInIoC { }

/// <summary>
/// Mock methods for registering dependencies used only by Bread.IoC SourceGenerator.
/// </summary>
public interface IBootstrapper
{
    void Regist<TImplementation>();
    void Regist<TImplementation>(TImplementation impl);
    void Regist<TInterface, TImplementation>();
}

public abstract class IoCContainer
{
    internal protected readonly Dictionary<Type, IResolver> Resolvers = new();

    protected abstract void Bootstrap(IBootstrapper ioc);
}


public static class IoC
{
    private static readonly Dictionary<Type, IResolver> Resolvers = new();

    public static void Init(params IoCContainer[] containers)
    {
        foreach (var c in containers) {
           foreach(var resolver in c.Resolvers) {
                Resolvers.AddOrReplace(resolver.Key, resolver.Value);
            }
        }

        foreach (var type in Resolvers.Keys) {
            if (type.IsSubclassOf(typeof(Controller))) {
                var value = Resolvers[type];
                _ = value.Resolve();
            }
        }
    }

    public static T Get<T>() where T : class
    {
        var o = Resolve(typeof(T)) ?? throw new TypeAccessException($"{nameof(T)} not found in IoC container");
        if (o is IHiddenInIoC) throw new InvalidProgramException("Hidden object can't resolve by IoC container");
        return (T)o;
    }


    public static object Resolve(Type type)
    {
        if (Resolvers.TryGetValue(type, out var entry)) {
            return entry.Resolve();
        }

        ExceptionHelper.ServiceIsNotRegistered(type.FullName);
        return null;
    }

    public static object Resolve(Type type, IOverrides overrides)
    {
        if (Resolvers.TryGetValue(type, out var entry)) {
            return entry.Resolve(overrides);
        }

        ExceptionHelper.ServiceIsNotRegistered(type.FullName);
        return null;
    }


   
    public static void Dispose()
    {
        foreach(var resolver in Resolvers) {
            resolver.Value.Dispose();
        }
    }

    static void AddOrReplace<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
    {
        if (dictionary.ContainsKey(key)) {
            dictionary.Remove(key);
            dictionary.Add(key, value);
            return;
        }

        dictionary.Add(key, value);
    }
}
